[dbt] データモデルの作成前や作成後にクエリを実行できるHooksを使ってみた
大阪オフィスの玉井です。
dbtで色々なデータモデルを作成する時、「このクエリを発行する直前に設定変更のクエリを実行したい」とか「このクエリが終わった直後に特定のクエリを流すようにしたい」みたいなこと、よくあると思います。
今回はそういう時に役立つ「Hooks」という機能を紹介します。
Hooksとは
記事の題名の通りなのですが、もう少しカタい言い方をすると「dbt modelの作成とはちょっと異なるタイミングでSQLを実行できる機能」という感じでしょうか。
今回、メインで紹介するのは下記の2種類です。
- pre-hook
- modelの作成直前に任意のクエリを実行できる
- seedやsnapshotでも使用可能
- post-hook
- modelの作成直後に任意のクエリを実行できる
- seedやsnapshotでも使用可能
ユースケースについては、以降の「やってみた」のところを読んでもらうと、しっくりくるかと思います。
やってみた
環境
- dbt Cloud
- Snowflake
まずは普通にdbt modelを作成する
下記のmodelを用意しました(source
として、既にTPCHのサンプルデータを定義済)。このクエリ自体はここに載っているサンプルクエリそのままです。
{{ config( materialized = 'view' ) }} SELECT l_returnflag ,l_linestatus ,SUM(l_quantity) AS sum_qty ,SUM(l_extendedprice) AS sum_base_price ,SUM(l_extendedprice * (1 - l_discount)) AS sum_disc_price ,SUM(l_extendedprice * (1 - l_discount) * (1 + l_tax)) AS sum_charge ,AVG(l_quantity) AS avg_qty ,AVG(l_extendedprice) AS avg_price ,AVG(l_discount) AS avg_disc ,COUNT(*) AS count_order FROM {{ source('tpch_sf1', 'lineitem') }} WHERE l_shipdate <= dateadd(day, - 90, to_date('1998-12-01')) GROUP BY l_returnflag ,l_linestatus ORDER BY l_returnflag ,l_linestatus
これといった問題はないので、普通に実行できるし、普通にこのビューが作成されます。
ただし、今後のこのビューの使い方によっては、このままだとちょっと不便です。わざわざdbtを使って整えたデータですから、当然、他のメンバーや他ツール等で、このデータを参照することになると思われます。しかし、今回使っているSnowflakeは、作成されたオブジェクトの所有者は、作成したロールがもつことになります。
このままだと、このデータにアクセスできるのは、所有者だけですので(権限の強いシステムロールは例外)、他のユーザー等にこのデータを使ってもらうためには、別途権限の設定を行う必要があります。
dbtで作ったデータモデルに対して、Snowflake側でいちいち権限の設定を行うのは面倒ですよね。そういう時にHooksを使うと、楽ができます。
post-hook
で、作成したデータモデルに権限を付与する
上記のデータモデルのconfig
部分に、post-hook
を加えます。
{{ config( materialized = 'view', post_hook=[ "grant select on {{ this }} to role dbt_role" ] ) }} ...
post-hook
は、このデータモデルが作成された直後に、追加で実行するSQLを書くことができます。今回は、dbt_role
というロールに対して、SELECT権限を付与するクエリを設定しました。これにより、このデータモデルが実行される度に、自動で権限設定のクエリが発行されるため、dbt側だけで、Snowflakeの権限設定まで行うことができるようになりました。
このデータモデルを実行してみます。しっかり、GRANT文も発行されていますね。
作成されたデータモデル(ビュー)の権限を確認したところ、しっかりSELECT権限がdbt_role
に付与されています。
pre-hook
で、データモデルの作成前に、使用する仮想ウェアハウスを変更する
Snowflakeの場合、色々な仮想ウェアハウスを使い分けることで、柔軟なワークロードが実現できます。dbtを使っていても、「このデータモデルの時は、こっちの仮想ウェアハウスで実行したいな」という時があります。そういう時はpre-hook
を使いましょう。
上記のデータモデルのconfig
部分に、pre-hook
を加えます。
{{ config( materialized = 'view', pre_hook=[ "use warehouse x_small_wh" ], post_hook=[ "grant select on {{ this }} to role dbt_role" ] ) }} ...
pre-hook
は、post-hook
の逆になります。つまり、データモデルの作成前に実行するSQLを書くことができます。今回は、USE WAREHOUSE文で、使用する仮想ウェアハウスを、別のものに変えます。
このデータモデルを実行してみます。しっかりUSE文が発行されています。
Snowflake側のクエリ履歴を確認したところ、ちゃんとこのデータモデルのCREATE文は、指定した別の仮想ウェアハウスが使われていました。
別の記述方法
今回はデータモデル自体にHooksを記述しましたが、dbt_project.yml
に別途記載することもできます。
models: project_name: tpc-h: +post-hook: - "grant select on {{ this }} to role dbt_role"
ちなみに、データモデル側のHooksとdbt_project.yml
側のHooksは共存するので、ご利用の際は注意してください。
別の種類のHooks
今回紹介したのは2種類ですが、実はあともう2種類のHooksがあります。
on-run-start
dbt run
等のコマンドの前に実行
on-run-end
dbt run
等のコマンドの後に実行
model単位ではなく、コマンド単位なので「一連のmodel作成全ての前と後」で実行したいクエリは、こちらで指定するとよさそうです。
おわりに
dbtは色々なDWHに対応していますが、この機能はすごくSnowflakeに向いていると思いました。今回の例以外でも、作成したデータモデルをデータシェアリングする時なんかも、post-hooksなんかで共有→権限付与、とスムーズに設定を行えそうです。
ちなみに、公式ドキュメントを読む限り、RedshiftだったらVACUUM処理なんかに使えるとのことでした。